This is a brief guide containing a reference of object methods in DinkLua as well as extra tables added to YeOldeDink that are not part of the base DinkLua spec. It assumes you've read the introduction from the author, and also have some existing knowledge of DinkC/engine workings, as well as some basic familiarity with object-oriented principles.
These can be troublesome as object function methods use the : semicolon operator, rather than dot as is used for table functions and therefore may be quite confusing.
The main table containing the bulk of engine features, "dink", is described separately in its own document.
Sprites are created using the create_sprite function as part of the "dink" table. Most DinkC-equivalent features are also under this table, however in the case of sprite properties, they are altered like so:
mysprite = dink.create_sprite(100, 100, 0, 100, 1) myothersprite = dink.create_sprite(120, 120, 6, 200, 1) -- getting a sprite number from a DinkC script using juggle var mythirdsprite = dink.get_sprite(global.juggle) -- add 5 to sprite's position mysprite.x = mysprite.x + 5 mysprite.y = 110 -- set sprite to not clip mysprite.noclip = true
mysprite = dink.create_sprite(100, 100, 0, 100, 1)
myothersprite = dink.create_sprite(120, 120, 6, 200, 1)
-- getting a sprite number from a DinkC script using juggle var
mythirdsprite = dink.get_sprite(global.juggle)
-- add 5 to sprite's position
mysprite.x = mysprite.x + 5
mysprite.y = 110
-- set sprite to not clip
mysprite.noclip = true
The rest of the sprite properties include:
mysprite.active = false -- used by some sprite brains mysprite.action = 2 mysprite.attack_hit_sound = 44 mysprite.attack_hit_sound_speed = 96000 -- time in ms before the sprite runs attack() mysprite.attack_wait = 10 -- base sequences mysprite.base_attack = 450 mysprite.base_die = 200 mysprite.base_death = 200 mysprite.base_hit = 100 mysprite.base_idle = 10 mysprite.base_walk = 20 mysprite.brain = brain.REPEAT or 6 mysprite.brain_parm = 5 mysprite.brain_parm2 = 10 mysprite.defense = 100 mysprite.dir = direction.SOUTH or 2 mysprite.disabled = false mysprite.distance = 100 mysprite.exp = 5000 mysprite.flying = true mysprite.follow = myothersprite mysprite.frame = 1 mysprite.seq = 100 mysprite.frame_delay = 33 mysprite.gold = 9000 mysprite.hard = true mysprite.hitpoints = 5 mysprite.move_nohard = true mysprite.mx = 4 mysprite.my = 5 mysprite.nocontrol = true mysprite.nodraw = false mysprite.nohit = true mysprite.notouch = false mysprite.pframe = 3 mysprite.picfreeze = true mysprite.pseq = 450 mysprite.que = -1000 mysprite.range = 10 mysprite.reverse = true mysprite.size = 150 mysprite.sound = 20 mysprite.strength = 200 mysprite.target = myothersprite mysprite.timing = 66 mysprite.touch_damage = -1 mysprite.kill = 1000 mysprite.freeze = true mysprite.custom["key"] = 121 mysprite.custom.otherkey = 212
mysprite.active = false
-- used by some sprite brains
mysprite.action = 2
mysprite.attack_hit_sound = 44
mysprite.attack_hit_sound_speed = 96000
-- time in ms before the sprite runs attack()
mysprite.attack_wait = 10
-- base sequences
mysprite.base_attack = 450
mysprite.base_die = 200
mysprite.base_death = 200
mysprite.base_hit = 100
mysprite.base_idle = 10
mysprite.base_walk = 20
mysprite.brain = brain.REPEAT or 6
mysprite.brain_parm = 5
mysprite.brain_parm2 = 10
mysprite.defense = 100
mysprite.dir = direction.SOUTH or 2
mysprite.disabled = false
mysprite.distance = 100
mysprite.exp = 5000
mysprite.flying = true
mysprite.follow = myothersprite
mysprite.frame = 1
mysprite.seq = 100
mysprite.frame_delay = 33
mysprite.gold = 9000
mysprite.hard = true
mysprite.hitpoints = 5
mysprite.move_nohard = true
mysprite.mx = 4
mysprite.my = 5
mysprite.nocontrol = true
mysprite.nodraw = false
mysprite.nohit = true
mysprite.notouch = false
mysprite.pframe = 3
mysprite.picfreeze = true
mysprite.pseq = 450
mysprite.que = -1000
mysprite.range = 10
mysprite.reverse = true
mysprite.size = 150
mysprite.sound = 20
mysprite.strength = 200
mysprite.target = myothersprite
mysprite.timing = 66
mysprite.touch_damage = -1
mysprite.kill = 1000
mysprite.freeze = true
mysprite.custom["key"] = 121
mysprite.custom.otherkey = 212
And so on and so on. sniffs
There are also some aliases for the properties preceded by "no" in the affirmative instead:
mysprite.clip = false -- sprite may be drawn over status bar mysprite.control = false mysprite.draw = false -- sprite won't be drawn mysprite.hit = false -- can't be attacked mysprite.touch = false -- untouchable
mysprite.clip = false
-- sprite may be drawn over status bar
mysprite.control = false
mysprite.draw = false
-- sprite won't be drawn
mysprite.hit = false
-- can't be attacked
mysprite.touch = false
-- untouchable
Values can also be retrieved like so:
local x = mysprite.x dink.debug(x) -- will emit 105 to the debug output
local x = mysprite.x
dink.debug(x)
-- will emit 105 to the debug output
As is usual in the world of Dink, most things don't make sense, and several other sprite modifications are performed with methods, along with the usual fare such as for talking and moving:
mysprite:freeze() mysprite:unfreeze() mysprite:kill_wait() mysprite:say("hi") mysprite:say_stop("bye") myothersprite:say_stop_npc("quack") mysprite:move(direction.SOUTH, 100, true) mysprite:move_stop(direction.NORTH, 100, false) mysprite:draw_hard() mysprite:hurt(5) mysprite:kill_shadow()
mysprite:freeze()
mysprite:unfreeze()
mysprite:kill_wait()
mysprite:say("hi")
mysprite:say_stop("bye")
myothersprite:say_stop_npc("quack")
mysprite:move(direction.SOUTH, 100, true)
mysprite:move_stop(direction.NORTH, 100, false)
mysprite:draw_hard()
mysprite:hurt(5)
mysprite:kill_shadow()
These can also be used on the convenience variables of current_sprite
and player
with the latter having a few specific ones of its own:
player:set_speed(5) player.get_speed player.can_walk_off_screen = true player.base_push = 500
player:set_speed(5)
player.get_speed
player.can_walk_off_screen = true
player.base_push = 500
Sound effects invoked with dink.playsound() will similarly return an object that has modifiable properties:
mysound = dink.playsound(44) myothersound = dink.get_soundbank(global.soundjuggle) -- retrieve a playsound invocation stored in a var mysound.vol = 50 -- half volume mysound.survive = true mysound.pan = -10 -- to the left a little mysound.hz = 22050 mysound.speed = 10 mysound.pause = true mysound.looppoint = 4 mysound.repeating = true
mysound = dink.playsound(44)
myothersound = dink.get_soundbank(global.soundjuggle)
-- retrieve a playsound invocation stored in a var
mysound.vol = 50
-- half volume
mysound.survive = true
mysound.pan = -10
-- to the left a little
mysound.hz = 22050
mysound.speed = 10
mysound.pause = true
mysound.looppoint = 4
mysound.repeating = true
Along with some that only return a value
local count = mysound.loopcount local position = mysound.pos local distvol = mysound.overall_vol -- for positional sounds
local count = mysound.loopcount
local position = mysound.pos
local distvol = mysound.overall_vol
-- for positional sounds
And similarly have some methods:
mysound:kill() mysound:fade_vol(90, 2) -- 90% volume over two seconds mysound:fade_pan(50, 5) -- half to the right over 5 seconds mysound:fade_speed(10, 5) -- 10% speed over 5 seconds mysound:schedule_pause(5) mysound:schedule_stop(8) mysound:oscillate_pan(-10, 0, 3) -- to the left and to centre over 3 seconds mysound:oscillate_vol(80, 90, 5) mysound:oscillate_speed(10, 100, 9)
mysound:kill()
mysound:fade_vol(90, 2)
-- 90% volume over two seconds
mysound:fade_pan(50, 5)
-- half to the right over 5 seconds
mysound:fade_speed(10, 5)
-- 10% speed over 5 seconds
mysound:schedule_pause(5)
mysound:schedule_stop(8)
mysound:oscillate_pan(-10, 0, 3)
-- to the left and to centre over 3 seconds
mysound:oscillate_vol(80, 90, 5)
mysound:oscillate_speed(10, 100, 9)
Please see below for the sound effect global object in 0.93.
Creating and accessing global variables is accomplished using the "global" table.
Creation:
global.create("name", 0)
global.create("name", 0)
Changing/retrieving the value:
global.name = 5 local myname = global.name
global.name = 5
local myname = global.name
Creation is performed by making a new menu object, adding choices to it, and then showing it, with the result stored in a magical variable.
local menu = dink.create_choice_menu() -- change params before showing it menu.y = 100 menu.title = "Hello!" menu.title_color = 5 -- add some choices local what = menu:add_choice("What is going on?") local where = menu:add_choice("Where am I?", global.location < 1) local mychoice = menu:show() if mychoice == what then current_sprite:say("I don't know") end if mychoice == where then current_sprite:say("It is a mystery") end
local menu = dink.create_choice_menu()
-- change params before showing it
menu.y = 100
menu.title = "Hello!"
menu.title_color = 5
-- add some choices
local what = menu:add_choice("What is going on?")
local where = menu:add_choice("Where am I?", global.location < 1)
local mychoice = menu:show()
if mychoice == what then
current_sprite:say("I don't know")
end
if mychoice == where then
current_sprite:say("It is a mystery")
end
Accessed by creating an object out of a sprite that has a corresponding editor sprite, and then altering its parameters.
local myoverride = current_sprite.editor_sprite myoverride.seq = 1 myoverride.frame = 10 myoverride.type = editor_type.KILL_RETURN_AFTER_ONE_MINUTE
local myoverride = current_sprite.editor_sprite
myoverride.seq = 1
myoverride.frame = 10
myoverride.type = editor_type.KILL_RETURN_AFTER_ONE_MINUTE
These may be passed to a sprite's brain parameter when prefixed with "brain" instead of using the number:
NONE = 0, PLAYER = 1, DUMB_SPRITE_BOUNCER = 2, DUCK = 3, PIG = 4, KILL_SEQ_DONE_LEAVE_LAST_FRAME = 5, REPEAT = 6, KILL_SEQ_DONE = 7, TEXT_SPRITE = 8, MONSTER_DIAGONAL = 9, MONSTER_NONDIAGONAL = 10, MISSILE = 11, BRAIN_PARM_SIZE_MATCH = 12, MOUSE = 13, BUTTON = 14, SHADOW = 15, SMART_PEOPLE = 16, MISSILE_KILL_SEQ_DONE = 17
NONE = 0,
PLAYER = 1,
DUMB_SPRITE_BOUNCER = 2,
DUCK = 3,
PIG = 4,
KILL_SEQ_DONE_LEAVE_LAST_FRAME = 5,
REPEAT = 6,
KILL_SEQ_DONE = 7,
TEXT_SPRITE = 8,
MONSTER_DIAGONAL = 9,
MONSTER_NONDIAGONAL = 10,
MISSILE = 11,
BRAIN_PARM_SIZE_MATCH = 12,
MOUSE = 13,
BUTTON = 14,
SHADOW = 15,
SMART_PEOPLE = 16,
MISSILE_KILL_SEQ_DONE = 17
To assign a duck brain for example, one would use
myothersprite.brain = brain.DUCK mysprite.brain = 10
myothersprite.brain = brain.DUCK
mysprite.brain = 10
Same as above except for directions:
SOUTH_WEST = 1, SOUTH = 2, SOUTH_EAST = 3, WEST = 4, EAST = 6, NORTH_WEST = 7, NORTH = 8, NORTH_EAST = 9
SOUTH_WEST = 1,
SOUTH = 2,
SOUTH_EAST = 3,
WEST = 4,
EAST = 6,
NORTH_WEST = 7,
NORTH = 8,
NORTH_EAST = 9
And used similarly as a substitution for the raw numbers:
myothersprite.dir = direction.EAST mysprite.dir = 8
myothersprite.dir = direction.EAST
mysprite.dir = 8
KILL_COMPLETELY = 1, DRAW_WITHOUT_HARDNESS = 2, DRAW_BACKGROUND_WITHOUT_HARDNESS = 3, DRAW_WITH_HARDNESS = 4, DRAW_BACKGROUND_WITH_HARDNESS = 5, KILL_RETURN_AFTER_FIVE_MINUTES = 6, KILL_RETURN_AFTER_THREE_MINUTES = 7, KILL_RETURN_AFTER_ONE_MINUTE = 8
KILL_COMPLETELY = 1,
DRAW_WITHOUT_HARDNESS = 2,
DRAW_BACKGROUND_WITHOUT_HARDNESS = 3,
DRAW_WITH_HARDNESS = 4,
DRAW_BACKGROUND_WITH_HARDNESS = 5,
KILL_RETURN_AFTER_FIVE_MINUTES = 6,
KILL_RETURN_AFTER_THREE_MINUTES = 7,
KILL_RETURN_AFTER_ONE_MINUTE = 8
mysprite.editor_sprite.type = editor_type.DRAW_WITH_HARDNESS
mysprite.editor_sprite.type = editor_type.DRAW_WITH_HARDNESS
etc
In 0.93 one has the ability to change the default sound effects for usually unalterable things such as ducks, pigs, the inventory, and the status bar.
To change these, one must use the "sfx" global object:
sfx.mouse.sound = 55 sfx.mouse.hz = 44100
sfx.mouse.sound = 55
sfx.mouse.hz = 44100
This will change the sound that plays on the title screen when one clicks on something that isn't a button sprite to sound slot 55 at 44.1kHz. Along with "mouse" there is also:
sfx.attack sfx.warp sfx.duck sfx.pig sfx.hurt
sfx.attack
sfx.warp
sfx.duck
sfx.pig
sfx.hurt
Which all have a corresponding sound and hz property for you to alter to your liking. In the case of the pigs, the set sound determines the lowest-numbered sound effect that will be played, meaning if set to 50, sounds 50 through to 54 will be used.
Similarly in the case of the hurt sound, it will be set sound +1 for the second hurt sound.
There is also one more in the base sfx table:
sfx.sprite_hz
sfx.sprite_hz
This determines the playback rate for sprites assigned a sound in the editor or via scripts which is 22KHz by default.
Much like the hz parameter for dink.playsound, setting hz to zero will cause a sound to be played back at its native speed, except for ducks and pigs which alter the playback rate depending on the size of the duck or pig.
For a size 100 duck or pig, the sample rate calculation is as follows:
duck_hz - (size * 50)
duck_hz - (size * 50)
Therefore, to play back at 44.1kHz, one should specify a sample rate of 49100 to sfx.duck.hz. The pigs will also apply an 800Hz variance to sound playback.
As the status bar, inventory, and choice menus have multiple sound effects to play back, they are grouped under sub-objects like so:
sfx.status.stats.hz = 0 sfx.status.gold.sound = 9 -- for when experience is incremented sfx.status.exp_tick.sound = 44 -- when experience counts up after new level sfx.status.exp_count.hz = 0 sfx.inventory.enter.sound = 60 sfx.inventory.move.sound = 61 sfx.inventory.select.sound = 62 sfx.inventory.exit.sound = 63 sfx.choice_menu.scroll.sound = 99 sfx.choice_menu.select.sound = 100
sfx.status.stats.hz = 0
sfx.status.gold.sound = 9
-- for when experience is incremented
sfx.status.exp_tick.sound = 44
-- when experience counts up after new level
sfx.status.exp_count.hz = 0
sfx.inventory.enter.sound = 60
sfx.inventory.move.sound = 61
sfx.inventory.select.sound = 62
sfx.inventory.exit.sound = 63
sfx.choice_menu.scroll.sound = 99
sfx.choice_menu.select.sound = 100
There are also two functions:
sfx.play(sound, hz, random, sprite, loop) sfx.halt_all()
sfx.play(sound, hz, random, sprite, loop)
sfx.halt_all()
Note that sfx.play() does not return a sound object and is purely for one-shotting with, and also doesn't work at all.
An alternative interface to features usually handled by dink.playsound()
.
music.play("wanderer.ogg", 4000) -- fade in ms param must be specified music.play_mod_order(50) -- tracker file pattern selection music.pause() music.resume() music.stop() music.fade_out(5000) local muspos = music.position local playingbool = music.is_playing() local myduration = music.duration() music.volume = 128 music.tempo = 0.5 -- only a few music formats support tempo music.loop = true -- equivalent to loopmidi music.load_soundfont("coolfont.sf2") -- loaded from root of dmoddir print(music.title) -- ID3 tag or filename usually
music.play("wanderer.ogg", 4000)
-- fade in ms param must be specified
music.play_mod_order(50)
-- tracker file pattern selection
music.pause()
music.resume()
music.stop()
music.fade_out(5000)
local muspos = music.position
local playingbool = music.is_playing()
local myduration = music.duration()
music.volume = 128
music.tempo = 0.5
-- only a few music formats support tempo
music.loop = true
-- equivalent to loopmidi
music.load_soundfont("coolfont.sf2")
-- loaded from root of dmoddir
print(music.title)
-- ID3 tag or filename usually
Second parameter to music.play() is a fade-in time, this is mandatory!
In 0.95, some of the inbuilt timer intervals may be overridden similar to the sound effects. All times are in milliseconds. These may alter things in unpredictable ways and should be approached with care.
timer.choice_arrows = 20 -- default is 100 for frame advance interval timer.inventory = 100 -- default is 400 for the inventory flasher timer.push = 100 -- default is 600 before the push animation plays timer.notouch = 1000 -- touch damage cooldown, default 400 timer.water_tiles = 8000 -- upper limit for water tile change interval, default 2000 timer.bow_pull = 10 -- adjusts time between bow pulls timer.bow = 100 -- adjusts bow frames. Changing this will require more frames timer.fade_resume = 2000 -- amount of time before a fade-down resumes the script, default 1000 timer.fade_time = 900 -- amount of time for a fade to complete, default 400
timer.choice_arrows = 20
-- default is 100 for frame advance interval
timer.inventory = 100
-- default is 400 for the inventory flasher
timer.push = 100
-- default is 600 before the push animation plays
timer.notouch = 1000
-- touch damage cooldown, default 400
timer.water_tiles = 8000
-- upper limit for water tile change interval, default 2000
timer.bow_pull = 10
-- adjusts time between bow pulls
timer.bow = 100
-- adjusts bow frames. Changing this will require more frames
timer.fade_resume = 2000
-- amount of time before a fade-down resumes the script, default 1000
timer.fade_time = 900
-- amount of time for a fade to complete, default 400
In 0.95 and later, there are two functions for directly manipulating the colour palette. As usual with 8-bit graphics in Dink, it will only work as expected in 8-bit display mode.
palette.setindex(int index, int r, int g, int b) palette.getindex(int index) -- change index 2 to red palette.setindex(2, 255, 0, 0) -- retrieve a table containing RGB triplet mycolour = palette.getindex(2) print(mycolour[1]) -- will show 255, with indices 2 and 3 zero
palette.setindex(int index, int r, int g, int b)
palette.getindex(int index)
-- change index 2 to red
palette.setindex(2, 255, 0, 0)
-- retrieve a table containing RGB triplet
mycolour = palette.getindex(2)
print(mycolour[1])
-- will show 255, with indices 2 and 3 zero
There are also two functions related to changing the default colour of choice menu text.
palette.set_choice_options(0, 255, 0) -- will change choices in the list to green palette.set_choice_hover(255, 0, 0) -- sets the hovered entry to red
palette.set_choice_options(0, 255, 0)
-- will change choices in the list to green
palette.set_choice_hover(255, 0, 0)
-- sets the hovered entry to red
The "game" object contains a variety of variables and functions that deal with the inner workings of the engine.
The first is "experience_limit" which determines the value to next level up at. If changing this value, it is advisable to develop your own level-up system that doesn't rely on stopping the game, as the experience upper limit is checked each frame.
The second is "map_width" which determines how wide the map is. Setting map_width to 1 will cause the player to walk to the next screen when traversing either right or down. The default is 32. The third, "animate_tiles" determines if traditional fire and water tiles ranges will animate or not.
game.experience_limit = 10 -- trigger next level up after gaining ten experience game.map_width = 2 -- make a long skinny map game.animate_tiles = false -- stop water from going up and down game.dinkc_divequals_fix = true -- allow for /= to work in DinkC scripts, or not game.alttext_display = false -- determine if the alternate text display for multiple colours in text should be enabled game.sprite_multiscript = true -- allows for sprites to have multiple scripts (experimental)
game.experience_limit = 10
-- trigger next level up after gaining ten experience
game.map_width = 2
-- make a long skinny map
game.animate_tiles = false
-- stop water from going up and down
game.dinkc_divequals_fix = true
-- allow for /= to work in DinkC scripts, or not
game.alttext_display = false
-- determine if the alternate text display for multiple colours in text should be enabled
game.sprite_multiscript = true
-- allows for sprites to have multiple scripts (experimental)
Plus some functions...
game.enable_hardened_mode() -- make the debug tools and inbuilt cheat inaccessible game.get_cheat() -- retrieve a bool indicating if the user has accessed the inbuilt cheat game.get_screen_hitmap() -- returns a table containing the raw 600x400 collision values game.is_controller_present() -- returns a bool indicating if the player has a controller plugged in game.getticks() -- gets the engine uptime in milliseconds
game.enable_hardened_mode()
-- make the debug tools and inbuilt cheat inaccessible
game.get_cheat()
-- retrieve a bool indicating if the user has accessed the inbuilt cheat
game.get_screen_hitmap()
-- returns a table containing the raw 600x400 collision values
game.is_controller_present()
-- returns a bool indicating if the player has a controller plugged in
game.getticks()
-- gets the engine uptime in milliseconds